#!/bin/bash
set -CeEu
set -o pipefail

SPATH="$(readlink -f "$0")"
SDIR="$(dirname "${SPATH}")"; export SDIR;

# shellcheck source=common.sh
. "${SDIR}/common.sh"

cd "${SDIR}"
pwd

# We want to not need to create clusters, which means no calling then with
# gcloud and kubectl. So we remove the call to "main" in install_asm, remove the
# "readonly" keywords, and then source the file so all of the functions are
# available. We can then overwrite kubectl and gcloud with functions that point
# to the fake scripts in this directory, and voila! We can mock binaries in the
# script.

head ../install_asm -n -1 | sed -e 's/readonly.*$//g' >| ./fake_install_asm
trap 'rm ./fake_install_asm' EXIT

. ./fake_install_asm

KUBE="$(readlink -f ./fake_kubectl)"
GCL="$(readlink -f ./fake_gcloud)"

clear_variables() {
  PROJECT_ID=""; export PROJECT_ID;
  CLUSTER_NAME=""; export CLUSTER_NAME;
  CLUSTER_LOCATION=""; export CLUSTER_LOCATION;
  MODE=""; export MODE;
  CA=""; export CA;

  OPERATOR_OVERLAY=""; export OPERATOR_OVERLAY;
  CUSTOM_OVERLAY=""; export CUSTOM_OVERLAY;
  ENABLE_ALL=0; export ENABLE_ALL;
  ENABLE_GCP_APIS=0; export ENABLE_GCP_APIS;
  ENABLE_GCP_IAM_ROLES=0; export ENABLE_GCP_IAM_ROLES;
  ENABLE_GCP_COMPONENTS=0; export ENABLE_GCP_COMPONENTS;
  ENABLE_CLUSTER_LABELS=0; export ENABLE_CLUSTER_LABELS;
  ENABLE_CLUSTER_ROLES=0; export ENABLE_CLUSTER_ROLES;
  PRINT_CONFIG=0; export PRINT_CONFIG;
  SERVICE_ACCOUNT=""; export SERVICE_ACCOUNT;
  KEY_FILE=""; export KEY_FILE;

  DRY_RUN=0; export DRY_RUN;
  ONLY_VALIDATE=0; export ONLY_VALIDATE;
  VERBOSE=0; export VERBOSE;
  _SLEEP_TIME=0; export _SLEEP_TIME;
}

nc() {
  return 0
}

kubectl() {
  local RETVAL; RETVAL=0;
  echo "Intercepted kubectl ${*}" >&2
  "${KUBE}" ${@} || RETVAL="${?}"
  return "${RETVAL}"
}

gcloud() {
  local RETVAL; RETVAL=0;
  echo "Intercepted gcloud ${*}" >&2
  "${GCL}" ${@} || RETVAL="${?}"
  return "${RETVAL}"
}

FATAL_EXITS=1
fatal() {
  echo "[FATAL(fake)]: ${1}" >&2
  if [[ "${FATAL_EXITS}" -eq 1 ]]; then
    exit 1
  fi
}

WARNED=0
warn_pause() {
  echo "[WARNING(fake)]: ${1}" >&2
  WARNED=1
}

warn() {
  echo "[WARNING(fake)]: ${1}" >&2
  WARNED=1
}

NODE_POOL=""
list_valid_pools() {
  echo "${NODE_POOL}"
}

sleep() {
  echo "sleeping ${*}" >&2
}

curl() {
  return 0
}

tar() {
  return 0
}

kpt() {
  if [[ "${1}" = "version" ]]; then
    echo "1.0.0"
  fi
  return 0
}

test_main() {
  local FAILURES; FAILURES=0;

  #################
  # This should pass
  #################

  local CMD
  CMD="-l this_should_pass"
  CMD="${CMD} -n this_should_pass"
  CMD="${CMD} -p this_should_pass"
  CMD="${CMD} -m install"
  CMD="${CMD} -c mesh_ca"
  CMD="${CMD} --only_validate"

  RETVAL=0
  clear_variables
  _="$(main ${CMD})" || RETVAL="${?}"

  if [[ "${RETVAL}" -eq 0 ]]; then
    echo "Passes on good case: PASS"
  else
    echo "Passes on good case: FAIL"
    ((++FAILURES))
  fi
  echo "***"

  #################
  # The owner role by itself should also pass
  #################

  CMD="-l this_should_pass"
  CMD="${CMD} -n this_should_pass"
  CMD="${CMD} -p owner_this_should_pass"
  CMD="${CMD} -m install"
  CMD="${CMD} -c mesh_ca"
  CMD="${CMD} --only_validate"

  RETVAL=0
  clear_variables
  _="$(_CI_I_AM_A_TEST_ROBOT=1 main ${CMD})" || RETVAL="${?}"

  if [[ "${RETVAL}" -eq 0 ]]; then
    echo "Passes with owner: PASS"
  else
    echo "Passes with owner: FAIL"
    ((++FAILURES))
  fi
  echo "***"

  #################
  # Should fail on non-existing project
  #################

  CMD="-l this_should_pass"
  CMD="${CMD} -n this_should_pass"
  CMD="${CMD} -p this_should_fail"
  CMD="${CMD} -m install"
  CMD="${CMD} -c mesh_ca"
  CMD="${CMD} --only-validate"

  RETVAL=0
  clear_variables
  _="$(main ${CMD})" || RETVAL="${?}"

  if [[ "${RETVAL}" -ne 0 ]]; then
    echo "Fails on nonexisting projects: PASS"
  else
    echo "Fails on nonexisting projects: FAIL"
    ((++FAILURES))
  fi
  echo "***"

  #################
  # Should fail on non-existing cluster
  #################

  CMD="-l this_should_pass"
  CMD="${CMD} -n this_should_fail"
  CMD="${CMD} -p this_should_pass"
  CMD="${CMD} -m install"
  CMD="${CMD} -c mesh_ca"
  CMD="${CMD} --only-validate"

  RETVAL=0
  clear_variables
  _="$(main ${CMD})" || RETVAL="${?}"

  if [[ "${RETVAL}" -ne 0 ]]; then
    echo "Fails on nonexisting clusters: PASS"
  else
    echo "Fails on nonexisting clusters: FAIL"
    ((++FAILURES))
  fi
  echo "***"

  #################
  # Should fail on bad CA or MODE
  #################

  CMD="-l this_should_pass"
  CMD="${CMD} -n this_should_pass"
  CMD="${CMD} -p this_should_pass"
  CMD="${CMD} -m this_should_fail"
  CMD="${CMD} -c mesh_ca"
  CMD="${CMD} --only_validate"

  RETVAL=0
  clear_variables
  _="$(main ${CMD})" || RETVAL="${?}"

  if [[ "${RETVAL}" -ne 0 ]]; then
    echo "Fails on bad mode: PASS"
  else
    echo "Fails on bad mode: FAIL"
    ((++FAILURES))
  fi
  echo "***"


  CMD="-l this_should_pass"
  CMD="${CMD} -n this_should_pass"
  CMD="${CMD} -p this_should_pass"
  CMD="${CMD} -m migrate"
  CMD="${CMD} -c this_should_fail"
  CMD="${CMD} --only_validate"

  RETVAL=0
  clear_variables
  _="$(main ${CMD})" || RETVAL="${?}"

  if [[ "${RETVAL}" -ne 0 ]]; then
    echo "Fails on bad CA: PASS"
  else
    echo "Fails on bad CA: FAIL"
    ((++FAILURES))
  fi
  echo "***"

  #################
  # Should fail on with only one of key/service account
  #################

  CMD="-l this_should_pass"
  CMD="${CMD} -n this_should_pass"
  CMD="${CMD} -p this_should_pass"
  CMD="${CMD} -m install"
  CMD="${CMD} -c mesh_ca"
  CMD="${CMD} -s service-account"
  CMD="${CMD} --only_validate"

  RETVAL=0
  clear_variables
  _="$(main ${CMD})" || RETVAL="${?}"

  if [[ "${RETVAL}" -ne 0 ]]; then
    echo "Fails on only SA: PASS"
  else
    echo "Fails on only SA: FAIL"
    ((++FAILURES))
  fi
  echo "***"


  CMD="-l this_should_pass"
  CMD="${CMD} -n this_should_pass"
  CMD="${CMD} -p this_should_pass"
  CMD="${CMD} -m install"
  CMD="${CMD} -c mesh_ca"
  CMD="${CMD} -k keyfile"
  CMD="${CMD} --only_validate"

  RETVAL=0
  clear_variables
  _="$(main ${CMD})" || RETVAL="${?}"

  if [[ "${RETVAL}" -ne 0 ]]; then
    echo "Fails on only key: PASS"
  else
    echo "Fails on only key: FAIL"
    ((++FAILURES))
  fi
  echo "***"

  #################
  # Should fail to do anything if it's in the wrong namespace
  #################

  CMD="-l this_should_pass_has_istio_wrong_namespace"
  CMD="${CMD} -n this_should_pass"
  CMD="${CMD} -p this_should_pass"
  CMD="${CMD} -m migrate"
  CMD="${CMD} -c mesh_ca"
  CMD="${CMD} --only_validate"

  RETVAL=0
  clear_variables
  _="$(main ${CMD})" || RETVAL="${?}"

  if [[ "${RETVAL}" -ne 0 ]]; then
    echo "Fails on migrating with Istio outside of istio-system: PASS"
  else
    echo "Fails on migrating with Istio outside of istio-system: FAIL"
    ((++FAILURES))
  fi
  echo "***"

  #################
  # Should fail if enable* passed with only-validate
  #################

  CMD="-l this_should_pass"
  CMD="${CMD} -n this_should_pass"
  CMD="${CMD} -p this_should_pass"
  CMD="${CMD} -m migrate"
  CMD="${CMD} -c mesh_ca"
  CMD="${CMD} -e --only_validate"

  RETVAL=0
  clear_variables
  _="$(main ${CMD})" || RETVAL="${?}"

  if [[ "${RETVAL}" -ne 0 ]]; then
    echo "Fails on using enable* with --only-validate: PASS"
  else
    echo "Fails on using enable* with --only-validate: FAIL"
    ((++FAILURES))
  fi
  echo "***"

  #################
  # Should fail if custom overlay is passed with --managed
  #################

  CMD="-l this_should_pass"
  CMD="${CMD} -n this_should_pass"
  CMD="${CMD} -p this_should_pass"
  CMD="${CMD} -m install"
  CMD="${CMD} --managed"
  CMD="${CMD} --custom-overlay foo.yaml"
  CMD="${CMD} -c mesh_ca"
  CMD="${CMD} --only_validate"

  RETVAL=0
  clear_variables
  _="$(main ${CMD})" || RETVAL="${?}"

  if [[ "${RETVAL}" -ne 0 ]]; then
    echo "Fails when passing --custom_overlay with --managed: PASS"
  else
    echo "Fails when passing --custom_overlay with --managed: FAIL"
    ((++FAILURES))
  fi
  echo "***"

  #################
  # Below we're testing that what permissions we use match the flags
  #################

  CMD="-l this_should_pass"
  CMD="${CMD} -n this_should_pass"
  CMD="${CMD} -p this_should_pass"
  CMD="${CMD} -m install"
  CMD="${CMD} -c mesh_ca"

  clear_variables
  parse_args ${CMD}

  if can_modify_cluster_roles \
    || can_modify_cluster_labels \
    || can_modify_gcp_apis \
    || can_modify_gcp_components \
    || can_modify_gcp_iam_roles; then
    echo "Permissions with no flags: FAIL"
    ((++FAILURES))
  else
    echo "Permissions with no flags: PASS"
  fi
  echo "***"

  clear_variables
  parse_args ${CMD} -e

  if ! can_modify_at_all \
    || ! can_modify_cluster_roles \
    || ! can_modify_cluster_labels \
    || ! can_modify_gcp_apis \
    || ! can_modify_gcp_components \
    || ! can_modify_gcp_iam_roles; then
    echo "Permissions with --enable-all flag: FAIL"
    ((++FAILURES))
  else
    echo "Permissions with --enable-all flag: PASS"
  fi
  echo "***"

  if can_register_cluster; then
    echo "Enable all shouldn't register to environ when unnecessary: FAIL"
    ((++FAILURES))
  else
    echo "Enable all shouldn't register to environ when unnecessary: PASS"
  fi
  echo "***"

  clear_variables
  parse_args ${CMD} --enable-cluster-labels

  if ! can_modify_at_all \
    || can_modify_cluster_roles \
    || ! can_modify_cluster_labels \
    || can_modify_gcp_apis \
    || can_register_cluster \
    || can_modify_gcp_components \
    || can_modify_gcp_iam_roles; then
    echo "Permissions with --enable-cluster-labels flag: FAIL"
    ((++FAILURES))
  else
    echo "Permissions with --enable-cluster-labels flag: PASS"
  fi
  echo "***"

  clear_variables
  parse_args ${CMD} --enable-cluster-roles

  if ! can_modify_at_all \
    || ! can_modify_cluster_roles \
    || can_modify_cluster_labels \
    || can_modify_gcp_apis \
    || can_register_cluster \
    || can_modify_gcp_components \
    || can_modify_gcp_iam_roles; then
    echo "Permissions with --enable-cluster-roles flag: FAIL"
    ((++FAILURES))
  else
    echo "Permissions with --enable-cluster-roles flag: PASS"
  fi
  echo "***"

  clear_variables
  parse_args ${CMD} --enable-gcp-apis

  if ! can_modify_at_all \
    || can_modify_cluster_roles \
    || can_modify_cluster_labels \
    || ! can_modify_gcp_apis \
    || can_register_cluster \
    || can_modify_gcp_components \
    || can_modify_gcp_iam_roles; then
    echo "Permissions with --enable-gcp-apis flag: FAIL"
    ((++FAILURES))
  else
    echo "Permissions with --enable-gcp-apis flag: PASS"
  fi
  echo "***"

  clear_variables
  parse_args ${CMD} --enable-gcp-components

  if ! can_modify_at_all \
    || can_modify_cluster_roles \
    || can_modify_cluster_labels \
    || can_modify_gcp_apis \
    || can_register_cluster \
    || ! can_modify_gcp_components \
    || can_modify_gcp_iam_roles; then
    echo "Permissions with --enable-gcp-components flag: FAIL"
    ((++FAILURES))
  else
    echo "Permissions with --enable-gcp-components flag: PASS"
  fi
  echo "***"

  clear_variables
  parse_args ${CMD} --enable-gcp-iam-roles

  if ! can_modify_at_all \
    || can_modify_cluster_roles \
    || can_modify_cluster_labels \
    || can_modify_gcp_apis \
    || can_register_cluster \
    || can_modify_gcp_components \
    || ! can_modify_gcp_iam_roles; then
    echo "Permissions with --enable-gcp-iam-roles flag: FAIL"
    ((++FAILURES))
  else
    echo "Permissions with --enable-gcp-iam-roles flag: PASS"
  fi
  echo "***"

  clear_variables
  parse_args ${CMD} --managed

  if ! can_modify_at_all \
    || ! can_modify_cluster_roles \
    || can_modify_cluster_labels \
    || can_modify_gcp_apis \
    || can_register_cluster \
    || ! can_modify_gcp_components \
    || ! can_modify_gcp_iam_roles; then
    echo "Permissions with --managed flag: FAIL"
    ((++FAILURES))
  else
    echo "Permissions with --managed flag: PASS"
  fi
  echo "***"

  clear_variables
  parse_args ${CMD} --enable-all --option vm --option hub_meshca

  if ! can_register_cluster; then
    echo "Enable all registers with environ when necessary: FAIL"
    ((++FAILURES))
  else
    echo "Enable all registers with environ when necessary: PASS"
  fi
  echo "***"

  #################
  # should be able to enable meshconfig separately
  #################

  clear_variables
  parse_args ${CMD} --enable-meshconfig-init

  if ! can_meshconfig_init || can_enable_gcp_components; then
    echo "Permissions with --enable-meshconfig-init flag: FAIL"
    ((++FAILURES))
  else
    echo "Permissions with --enable-meshconfig-init flag: PASS"
  fi
  echo "***"

  clear_variables
  parse_args ${CMD} --enable-all --option vm --option hub_meshca

  if ! can_register_cluster; then
    echo "Enable all registers with environ when necessary: FAIL"
    ((++FAILURES))
  else
    echo "Enable all registers with environ when necessary: PASS"
  fi
  echo "***"

  #################
  # find_missing_strings shouldn't return anything with a trailing comma
  #################

  read -r HAYSTACK <<EOF
one
two
three
four
EOF

  ANS="$(find_missing_strings "one,four" "${HAYSTACK}")"
  echo "Got ${ANS} when testing find_missing_strings"
  if [[ "${ANS}" != "one,four" ]]; then
    echo "find_missing_strings is formatted properly: FAIL"
    ((++FAILURES))
  else
    echo "find_missing_strings is formatted properly: PASS"
  fi
  echo "***"

  #################
  # local_iam_user should pick type correctly when domain is gserviceaccount.com
  #################

  PROJECT_ID="user"
  TYPE="$(local_iam_user | cut -f 1 -d":")"
  echo "Got type ${TYPE}"

  if [[ "${TYPE}" != "user" ]]; then
    echo "local_iam_user detects user: FAIL"
    ((++FAILURES))
  else
    echo "local_iam_user detects user: PASS"
  fi
  echo "***"

  PROJECT_ID="sa"
  TYPE="$(local_iam_user | cut -f 1 -d":")"
  echo "Got type ${TYPE}"

  if [[ "${TYPE}" != "serviceAccount" ]]; then
    echo "local_iam_user detects SA: FAIL"
    ((++FAILURES))
  else
    echo "local_iam_user detects SA: PASS"
  fi
  echo "***"

  PROJECT_ID="notloggedin"

  FATAL_EXITS=0
  if [[ "$(local_iam_user)" != "user:" ]]; then
    echo "local_iam_user detects not logged in state: FAIL"
    ((++FAILURES))
  else
    echo "local_iam_user detects not logged in state: PASS"
  fi
  echo "***"
  FATAL_EXITS=1

  #################
  # node pool validation should handle zonal, regional pools with mixed types
  #################

  WARNED=0
  NODE_POOL='[
{
  "autoscaling": {
    "enabled": false
  },
  "config": {
    "machineType": "e2-standard-4"
  },
  "initialNodeCount": 1,
  "locations": [
    "us-central1-x",
    "us-central1-y",
    "us-central1-z"
  ]
},
{
  "autoscaling": {
    "enabled": false
  },
  "config": {
    "machineType": "e2-medium"
  },
  "initialNodeCount": 1,
  "locations": [
    "us-central1-z"
  ]
}
]'

  validate_node_pool

  if [[ "${WARNED}" -eq 1 ]]; then
    echo "validate_node_pool handles multiple pool location/types: FAIL"
    ((++FAILURES))
  else
    echo "validate_node_pool handles multiple pool location/types: PASS"
  fi
  echo "***"

  #################
  # CLI dependency validation should fail when compatibility variables are unset
  #################

  WARNED=0
  FATAL_EXITS=0
  AKPT="kpt"
  AGCLOUD=""
  AKUBECTL="kubectl"
  validate_cli_dependencies
  FATAL_EXITS=1

  if [[ "${WARNED}" -eq 0 ]]; then
    echo "validate_cli_dependencies handles compatibility variables: FAIL"
    ((++FAILURES))
  else
    echo "validate_cli_dependencies handles compatibility variables: PASS"
  fi
  echo "***"

  #################
  # kpt should be downloaded if the environment version is not 0.x
  #################

  if ! needs_kpt; then
    echo "needs_kpt is true when kpt is too new: FAIL"
    ((++FAILURES))
  else
    echo "needs_kpt is true when kpt is too new: PASS"
  fi
  echo "***"

  #################
  echo "There were ${FAILURES} failures."
  exit "${FAILURES}"
}

test_main "$@"
